package it.polito.connectedcar.message;

import it.polito.connectedcar.tcc.TCCTrafficLightPhaseChange;
import it.polito.connectedcar.tcc.zone.Token;
import it.polito.connectedcar.tcc.zone.TokenLink;
import it.polito.connectedcar.util.Constants;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Implementation class used to wrap informaton to be sent by a <b>TCC</b> to a
 * vehicle. A TCC can send many type of messages to a vehicles depending on the
 * type of information contained.<br/>
 * <ul>
 * <li>
 * <b>TCC_VEHICLES_LIST_MESSAGE_CODE</b> : normally after an update message
 * received by the TCC from a vehicles , the TCC sends to such vehicle the list
 * of all the vehicles'addresses it has to talk with until next update. vehicles
 * belonging to different tokens will be grouped in lists of addresses , each of
 * them preceded by the belonging token in order to be recognized by the
 * receiver.</li>
 * <li>
 * <b>TCC_CHANGE_TOKEN_MESSAGE_CODE</b> : the TCC informs a vehicle that it has
 * to change its actual token to the one contained in this message. Normally it
 * is used when a TCC discovers that a previously outside vehicle has now
 * entered in one of its zones, thus informing it.</li>
 * <li>
 * <b>TCC_NEW_ZONE_MESSAGE_CODE</b> : When a TCC discovers that a vehicle is
 * approaching another Zone( Token ) , the TCC has to inform such vehicle about
 * how to contact the next TCC in charge of that token. A list of {Token-TCC
 * address} is sent to the vehicle.</li>
 * <li><b>TCC_TRAFFIC_LIGHT_UPDATE</b> : Depending on some information , the TCC
 * may have to send also traffic lights information to the vehicles to let him
 * predict some behaviors. The infomation to be sent are the same as those
 * contained in {@link TCCFromTrafficLightMessage}.</li>.
 * </ul>
 * 
 * @author Nicola Aresta
 * 
 */
public class TCCToVehicleMessage extends OutgoingMessage {

	private Map<Token, Set<InetSocketAddress>> vehicles;
	private List<TokenLink> tokenLinks;
	private TCCTrafficLightPhaseChange trafficLightPhaseChange;
	private Token changeToken;

	public TCCToVehicleMessage(byte messageCode) {
		super(Constants.TCC_TYPE_CODE, messageCode);
	}

	@Override
	protected void convertToData(Storage st, boolean forTCC, Token t) {

		switch (actionCode) {
			case Constants.TCC_VEHICLES_LIST_MESSAGE_CODE :
				st.writeByte(vehicles.size());
				for (Entry<Token, Set<InetSocketAddress>> entry : vehicles.entrySet()) {
					// write the token
					for (int i = 0; i < entry.getKey().getBytes().length; i++)
						st.writeByte(entry.getKey().getBytes()[i]);
					// write the vehicles addresses
					st.writeInt(entry.getValue().size());
					for (InetSocketAddress vehicleAddr : entry.getValue()) {
						byte[] addr = vehicleAddr.getAddress().getAddress();
						for (int j = 0; j < addr.length; j++)
							st.writeByte(addr[j]);

						st.writeInt(vehicleAddr.getPort());
					}
				}
				break;
			case Constants.TCC_CHANGE_TOKEN_MESSAGE_CODE :
				for (int i = 0; i < changeToken.getBytes().length; i++)
					st.writeByte(changeToken.getBytes()[i]);
				break;
			case Constants.TCC_NEW_ZONE_MESSAGE_CODE :

				st.writeInt(tokenLinks.size());
				for (TokenLink tl : tokenLinks) {
					byte[] tokenBytes = tl.getToken().getBytes();
					st.writeInt(tokenBytes.length);
					for (int j = 0; j < tokenBytes.length; j++) {
						st.writeByte(tokenBytes[j]);
					}

					InetSocketAddress tccAddress = tl.getTCCAddress();

					byte[] address = tccAddress.getAddress().getAddress();
					st.writeByte(address.length);

					for (int i = 0; i < address.length; i++) {
						st.writeByte(address[i]);
					}
					st.writeInt(tccAddress.getPort());
				}

				break;
			case Constants.TCC_TRAFFIC_LIGHT_UPDATE :

				st.writeStringASCII(trafficLightPhaseChange.getCurrentState());
				st.writeStringASCII(trafficLightPhaseChange.getNextState());
				st.writeLong(trafficLightPhaseChange.getLastChangeTimeStamp());
				st.writeLong(trafficLightPhaseChange.getAssumedTimeToExchange());
				st.writeDouble(trafficLightPhaseChange.getPosition().getX());
				st.writeDouble(trafficLightPhaseChange.getPosition().getY());

			default :
				break;
		}

	}

	public Map<Token, Set<InetSocketAddress>> getVehicles() {
		return vehicles;
	}

	public void setVehicles(Map<Token, Set<InetSocketAddress>> vehicles) {
		this.vehicles = vehicles;
	}

	public void setTrafficLight(
			TCCTrafficLightPhaseChange nextTrafficLightOnLane) {
		this.trafficLightPhaseChange = nextTrafficLightOnLane;

	}

	public List<TokenLink> getTokenLinks() {
		return tokenLinks;
	}

	public void setTokenLinks(List<TokenLink> tokenLinks) {
		this.tokenLinks = tokenLinks;
	}

	public Token getChangeToken() {
		return changeToken;
	}

	public void setChangeToken(Token changeToken) {
		this.changeToken = changeToken;
	}

}
